#include "wx/wxprec.h"
#include "wx/scrolwin.h"
#include "wx/recguard.h"

#ifdef __WXMOTIF__

#ifdef __VMS__

# pragma message disable nosimpint
#endif
#include <Xm/Xm.h>
#ifdef __VMS__
# pragma message enable nosimpint
#endif
#endif

class wxScrollHelperEvtHandler : public wxEvtHandler {
  public:
    wxScrollHelperEvtHandler( wxScrollHelper *scrollHelper ) {
      m_scrollHelper = scrollHelper;
    }

    virtual bool ProcessEvent( wxEvent& event );
    void ResetDrawnFlag() { m_hasDrawnWindow = false; }
  private:
    wxScrollHelper *m_scrollHelper;
    bool m_hasDrawnWindow;
    DECLARE_NO_COPY_CLASS( wxScrollHelperEvtHandler )
};

#if wxUSE_TIMER

class wxAutoScrollTimer : public wxTimer {
  public:
    wxAutoScrollTimer( wxWindow *winToScroll, wxScrollHelper *scroll, wxEventType eventTypeToSend, int pos, int orient );
    virtual void Notify();
  private:
    wxWindow *m_win;
    wxScrollHelper *m_scrollHelper;
    wxEventType m_eventType;
    int m_pos,
        m_orient;

    DECLARE_NO_COPY_CLASS( wxAutoScrollTimer )
};

wxAutoScrollTimer::wxAutoScrollTimer( wxWindow *winToScroll,
                                      wxScrollHelper *scroll,
                                      wxEventType eventTypeToSend,
                                      int pos, int orient ) {
  m_win = winToScroll;
  m_scrollHelper = scroll;
  m_eventType = eventTypeToSend;
  m_pos = pos;
  m_orient = orient;
}

void wxAutoScrollTimer::Notify() {
  if( wxWindow::GetCapture() != m_win ) {
    Stop();
  } else {
    wxScrollWinEvent event1( m_eventType, m_pos, m_orient );
    event1.SetEventObject( m_win );
    if( m_scrollHelper->SendAutoScrollEvents( event1 ) &&
        m_win->GetEventHandler()->ProcessEvent( event1 ) ) {
      wxMouseEvent event2( wxEVT_MOTION );
      wxGetMousePosition( &event2.m_x, &event2.m_y );
      wxWindow *parentTop = m_win;
      while( parentTop->GetParent() ) {
        parentTop = parentTop->GetParent();
      }
      wxPoint ptOrig = parentTop->GetPosition();
      event2.m_x -= ptOrig.x;
      event2.m_y -= ptOrig.y;
      event2.SetEventObject( m_win );
      m_win->GetEventHandler()->ProcessEvent( event2 );
    } else {
      Stop();
    }
  }
}
#endif

bool wxScrollHelperEvtHandler::ProcessEvent( wxEvent& event ) {
  wxEventType evType = event.GetEventType();
  m_hasDrawnWindow = true;
  bool processed = wxEvtHandler::ProcessEvent( event );
  if( evType == wxEVT_SIZE ) {
    m_scrollHelper->HandleOnSize( ( wxSizeEvent & )event );
    return true;
  }
  if( processed ) {
    if( m_hasDrawnWindow || event.IsCommandEvent() ) {
      return true;
    }
  }
  event.Skip( false );
  if( evType == wxEVT_PAINT ) {
    m_scrollHelper->HandleOnPaint( ( wxPaintEvent & )event );
    return true;
  }
  if( evType == wxEVT_CHILD_FOCUS ) {
    m_scrollHelper->HandleOnChildFocus( ( wxChildFocusEvent & )event );
    return true;
  }
  if( evType == wxEVT_SCROLLWIN_TOP ||
      evType == wxEVT_SCROLLWIN_BOTTOM ||
      evType == wxEVT_SCROLLWIN_LINEUP ||
      evType == wxEVT_SCROLLWIN_LINEDOWN ||
      evType == wxEVT_SCROLLWIN_PAGEUP ||
      evType == wxEVT_SCROLLWIN_PAGEDOWN ||
      evType == wxEVT_SCROLLWIN_THUMBTRACK ||
      evType == wxEVT_SCROLLWIN_THUMBRELEASE ) {
    m_scrollHelper->HandleOnScroll( ( wxScrollWinEvent & )event );
    return !event.GetSkipped();
  }
  if( evType == wxEVT_ENTER_WINDOW ) {
    m_scrollHelper->HandleOnMouseEnter( ( wxMouseEvent & )event );
  } else if( evType == wxEVT_LEAVE_WINDOW ) {
    m_scrollHelper->HandleOnMouseLeave( ( wxMouseEvent & )event );
  }
  #if wxUSE_MOUSEWHEEL
  else if( evType == wxEVT_MOUSEWHEEL ) {
    m_scrollHelper->HandleOnMouseWheel( ( wxMouseEvent & )event );
    return true;
  }
  #endif
  else if( evType == wxEVT_CHAR ) {
    m_scrollHelper->HandleOnChar( ( wxKeyEvent & )event );
    return !event.GetSkipped();
  }
  return false;
}

wxScrollHelper::wxScrollHelper( wxWindow *win ) {
  wxASSERT_MSG( win, _T( "associated window can't be NULL in wxScrollHelper" ) );
  m_xScrollPixelsPerLine =
    m_yScrollPixelsPerLine =
      m_xScrollPosition =
        m_yScrollPosition =
          m_xScrollLines =
            m_yScrollLines =
              m_xScrollLinesPerPage =
                m_yScrollLinesPerPage = 0;
  m_xScrollingEnabled =
    m_yScrollingEnabled = true;
  m_scaleX =
    m_scaleY = 1.0;
  #if wxUSE_MOUSEWHEEL
  m_wheelRotation = 0;
  #endif
  m_win =
    m_targetWindow = ( wxWindow * )NULL;
  m_timerAutoScroll = ( wxTimer * )NULL;
  m_handler = NULL;
  m_win = win;
  m_win->SetScrollHelper( this );
  DoSetTargetWindow( win );
}

wxScrollHelper::~wxScrollHelper() {
  StopAutoScrolling();
  DeleteEvtHandler();
}

void wxScrollHelper::SetScrollbars( int pixelsPerUnitX, int pixelsPerUnitY, int noUnitsX, int noUnitsY,
                                    int xPos, int yPos, bool noRefresh ) {
  int xpos, ypos;
  CalcUnscrolledPosition( xPos, yPos, &xpos, &ypos );
  bool do_refresh = ( ( noUnitsX != 0 && m_xScrollLines == 0 ) ||
                      ( noUnitsX < m_xScrollLines && xpos > pixelsPerUnitX * noUnitsX ) ||
                      ( noUnitsY != 0 && m_yScrollLines == 0 ) ||
                      ( noUnitsY < m_yScrollLines && ypos > pixelsPerUnitY * noUnitsY ) ||
                      ( xPos != m_xScrollPosition ) ||
                      ( yPos != m_yScrollPosition ) );
  m_xScrollPixelsPerLine = pixelsPerUnitX;
  m_yScrollPixelsPerLine = pixelsPerUnitY;
  m_xScrollPosition = xPos;
  m_yScrollPosition = yPos;
  int w = noUnitsX * pixelsPerUnitX;
  int h = noUnitsY * pixelsPerUnitY;
  m_targetWindow->SetVirtualSize( w ? w : wxDefaultCoord, h ? h : wxDefaultCoord );
  if( do_refresh && !noRefresh ) {
    m_targetWindow->Refresh( true, GetScrollRect() );
  }
  if( m_targetWindow != m_win ) {
    AdjustScrollbars();
  }
}

void wxScrollHelper::DeleteEvtHandler() {
  if( m_win && m_handler ) {
    if( m_win->RemoveEventHandler( m_handler ) ) {
      delete m_handler;
    }
    m_handler = NULL;
  }
}

void wxScrollHelper::DoSetTargetWindow( wxWindow *target ) {
  m_targetWindow = target;
  if( m_targetWindow == m_win ) {
    DeleteEvtHandler();
    m_handler = new wxScrollHelperEvtHandler( this );
    m_targetWindow->PushEventHandler( m_handler );
  }
}

void wxScrollHelper::SetTargetWindow( wxWindow *target ) {
  wxCHECK_RET( target, wxT( "目标窗口不能是 NULL" ) );
  if( target == m_targetWindow ) {
    return;
  }
  DoSetTargetWindow( target );
}

wxWindow *wxScrollHelper::GetTargetWindow() const {
  return m_targetWindow;
}

void wxScrollHelper::HandleOnScroll( wxScrollWinEvent& event ) {
  int nScrollInc = CalcScrollInc( event );
  if( nScrollInc == 0 ) {
    event.Skip();
    return;
  }
  bool needsRefresh = false;
  int dx = 0,
      dy = 0;
  int orient = event.GetOrientation();
  if( orient == wxHORIZONTAL ) {
    if( m_xScrollingEnabled ) {
      dx = -m_xScrollPixelsPerLine * nScrollInc;
    } else {
      needsRefresh = true;
    }
  } else {
    if( m_yScrollingEnabled ) {
      dy = -m_yScrollPixelsPerLine * nScrollInc;
    } else {
      needsRefresh = true;
    }
  }
  if( !needsRefresh ) {
    m_targetWindow->Update();
  }
  if( orient == wxHORIZONTAL ) {
    m_xScrollPosition += nScrollInc;
    m_win->SetScrollPos( wxHORIZONTAL, m_xScrollPosition );
  } else {
    m_yScrollPosition += nScrollInc;
    m_win->SetScrollPos( wxVERTICAL, m_yScrollPosition );
  }
  if( needsRefresh ) {
    m_targetWindow->Refresh( true, GetScrollRect() );
  } else {
    m_targetWindow->ScrollWindow( dx, dy, GetScrollRect() );
  }
}

int wxScrollHelper::CalcScrollInc( wxScrollWinEvent& event ) {
  int pos = event.GetPosition();
  int orient = event.GetOrientation();
  int nScrollInc = 0;
  if( event.GetEventType() == wxEVT_SCROLLWIN_TOP ) {
    if( orient == wxHORIZONTAL ) {
      nScrollInc = - m_xScrollPosition;
    } else {
      nScrollInc = - m_yScrollPosition;
    }
  } else if( event.GetEventType() == wxEVT_SCROLLWIN_BOTTOM ) {
    if( orient == wxHORIZONTAL ) {
      nScrollInc = m_xScrollLines - m_xScrollPosition;
    } else {
      nScrollInc = m_yScrollLines - m_yScrollPosition;
    }
  } else if( event.GetEventType() == wxEVT_SCROLLWIN_LINEUP ) {
    nScrollInc = -1;
  } else if( event.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN ) {
    nScrollInc = 1;
  } else if( event.GetEventType() == wxEVT_SCROLLWIN_PAGEUP ) {
    if( orient == wxHORIZONTAL ) {
      nScrollInc = -GetScrollPageSize( wxHORIZONTAL );
    } else {
      nScrollInc = -GetScrollPageSize( wxVERTICAL );
    }
  } else if( event.GetEventType() == wxEVT_SCROLLWIN_PAGEDOWN ) {
    if( orient == wxHORIZONTAL ) {
      nScrollInc = GetScrollPageSize( wxHORIZONTAL );
    } else {
      nScrollInc = GetScrollPageSize( wxVERTICAL );
    }
  } else if( ( event.GetEventType() == wxEVT_SCROLLWIN_THUMBTRACK ) ||
             ( event.GetEventType() == wxEVT_SCROLLWIN_THUMBRELEASE ) ) {
    if( orient == wxHORIZONTAL ) {
      nScrollInc = pos - m_xScrollPosition;
    } else {
      nScrollInc = pos - m_yScrollPosition;
    }
  }
  if( orient == wxHORIZONTAL ) {
    if( m_xScrollPixelsPerLine > 0 ) {
      if( m_xScrollPosition + nScrollInc < 0 ) {
        nScrollInc = -m_xScrollPosition;
      } else {
        const int posMax = m_xScrollLines - m_xScrollLinesPerPage;
        if( m_xScrollPosition + nScrollInc > posMax ) {
          nScrollInc = posMax - m_xScrollPosition;
        }
      }
    } else {
      m_targetWindow->Refresh( true, GetScrollRect() );
    }
  } else {
    if( m_yScrollPixelsPerLine > 0 ) {
      if( m_yScrollPosition + nScrollInc < 0 ) {
        nScrollInc = -m_yScrollPosition;
      } else {
        const int posMax = m_yScrollLines - m_yScrollLinesPerPage;
        if( m_yScrollPosition + nScrollInc > posMax ) {
          nScrollInc = posMax - m_yScrollPosition;
        }
      }
    } else {
      m_targetWindow->Refresh( true, GetScrollRect() );
    }
  }
  return nScrollInc;
}


void wxScrollHelper::AdjustScrollbars() {
  static wxRecursionGuardFlag s_flagReentrancy;
  wxRecursionGuard guard( s_flagReentrancy );
  if( guard.IsInside() ) {
    return;
  }
  int w = 0, h = 0;
  int oldw, oldh;
  int oldXScroll = m_xScrollPosition;
  int oldYScroll = m_yScrollPosition;
  int iterationCount = 0;
  const int iterationMax = 5;
  do {
    iterationCount ++;
    GetTargetSize( &w, 0 );
    int linesPerPage;
    if( m_xScrollPixelsPerLine == 0 ) {
      m_xScrollLines = 0;
      m_xScrollPosition = 0;
      linesPerPage = 0;
    } else {
      const int wVirt = m_targetWindow->GetVirtualSize().GetWidth();
      m_xScrollLines = ( wVirt + m_xScrollPixelsPerLine - 1 ) / m_xScrollPixelsPerLine;
      linesPerPage = w / m_xScrollPixelsPerLine;
      if( ( linesPerPage < m_xScrollLines ) && ( w >= wVirt ) ) {
        ++linesPerPage;
      }
      if( linesPerPage >= m_xScrollLines ) {
        linesPerPage =
          m_xScrollLines =
            m_xScrollPosition = 0;
      } else {
        if( linesPerPage < 1 ) {
          linesPerPage = 1;
        }
        const int posMax = m_xScrollLines - linesPerPage;
        if( m_xScrollPosition > posMax ) {
          m_xScrollPosition = posMax;
        } else if( m_xScrollPosition < 0 ) {
          m_xScrollPosition = 0;
        }
      }
    }
    m_win->SetScrollbar( wxHORIZONTAL, m_xScrollPosition, linesPerPage, m_xScrollLines );
    SetScrollPageSize( wxHORIZONTAL, linesPerPage );
    GetTargetSize( 0, &h );
    if( m_yScrollPixelsPerLine == 0 ) {
      m_yScrollLines = 0;
      m_yScrollPosition = 0;
      linesPerPage = 0;
    } else {
      const int hVirt = m_targetWindow->GetVirtualSize().GetHeight();
      m_yScrollLines = ( hVirt + m_yScrollPixelsPerLine - 1 ) / m_yScrollPixelsPerLine;
      linesPerPage = h / m_yScrollPixelsPerLine;
      if( ( linesPerPage < m_yScrollLines ) && ( h >= hVirt ) ) {
        ++linesPerPage;
      }
      if( linesPerPage >= m_yScrollLines ) {
        linesPerPage =
          m_yScrollLines =
            m_yScrollPosition = 0;
      } else {
        if( linesPerPage < 1 ) {
          linesPerPage = 1;
        }
        const int posMax = m_yScrollLines - linesPerPage;
        if( m_yScrollPosition > posMax ) {
          m_yScrollPosition = posMax;
        } else if( m_yScrollPosition < 0 ) {
          m_yScrollPosition = 0;
        }
      }
    }
    m_win->SetScrollbar( wxVERTICAL, m_yScrollPosition,
                         linesPerPage, m_yScrollLines );
    SetScrollPageSize( wxVERTICAL, linesPerPage );
    oldw = w;
    oldh = h;
    GetTargetSize( &w, &h );
  } while( ( w != oldw || h != oldh ) && ( iterationCount < iterationMax ) );
  #ifdef __WXMOTIF__
  if( m_targetWindow->GetWindowStyle() & wxRETAINED ) {
    Display* dpy = XtDisplay( ( Widget )m_targetWindow->GetMainWidget() );
    int totalPixelWidth = m_xScrollLines * m_xScrollPixelsPerLine;
    int totalPixelHeight = m_yScrollLines * m_yScrollPixelsPerLine;
    if( m_targetWindow->GetBackingPixmap() &&
        !( ( m_targetWindow->GetPixmapWidth() == totalPixelWidth ) &&
           ( m_targetWindow->GetPixmapHeight() == totalPixelHeight ) ) ) {
      XFreePixmap( dpy, ( Pixmap ) m_targetWindow->GetBackingPixmap() );
      m_targetWindow->SetBackingPixmap( ( WXPixmap ) 0 );
    }
    if( !m_targetWindow->GetBackingPixmap() &&
        ( m_xScrollLines != 0 ) && ( m_yScrollLines != 0 ) ) {
      int depth = wxDisplayDepth();
      m_targetWindow->SetPixmapWidth( totalPixelWidth );
      m_targetWindow->SetPixmapHeight( totalPixelHeight );
      m_targetWindow->SetBackingPixmap( ( WXPixmap ) XCreatePixmap( dpy, RootWindow( dpy, DefaultScreen( dpy ) ),
                                        m_targetWindow->GetPixmapWidth(), m_targetWindow->GetPixmapHeight(), depth ) );
    }
  }
  #endif
  if( oldXScroll != m_xScrollPosition ) {
    if( m_xScrollingEnabled )
      m_targetWindow->ScrollWindow( m_xScrollPixelsPerLine * ( oldXScroll - m_xScrollPosition ), 0,
                                    GetScrollRect() );
    else
    { m_targetWindow->Refresh( true, GetScrollRect() ); }
  }
  if( oldYScroll != m_yScrollPosition ) {
    if( m_yScrollingEnabled )
      m_targetWindow->ScrollWindow( 0, m_yScrollPixelsPerLine * ( oldYScroll - m_yScrollPosition ),
                                    GetScrollRect() );
    else
    { m_targetWindow->Refresh( true, GetScrollRect() ); }
  }
}

void wxScrollHelper::DoPrepareDC( wxDC& dc ) {
  wxPoint pt = dc.GetDeviceOrigin();
  if( m_win->GetLayoutDirection() == wxLayout_RightToLeft )
    dc.SetDeviceOrigin( pt.x + m_xScrollPosition * m_xScrollPixelsPerLine,
                        pt.y - m_yScrollPosition * m_yScrollPixelsPerLine );
  else
    dc.SetDeviceOrigin( pt.x - m_xScrollPosition * m_xScrollPixelsPerLine,
                        pt.y - m_yScrollPosition * m_yScrollPixelsPerLine );
  dc.SetUserScale( m_scaleX, m_scaleY );
}

void wxScrollHelper::SetScrollRate( int xstep, int ystep ) {
  int old_x = m_xScrollPixelsPerLine * m_xScrollPosition;
  int old_y = m_yScrollPixelsPerLine * m_yScrollPosition;
  m_xScrollPixelsPerLine = xstep;
  m_yScrollPixelsPerLine = ystep;
  int new_x = m_xScrollPixelsPerLine * m_xScrollPosition;
  int new_y = m_yScrollPixelsPerLine * m_yScrollPosition;
  m_win->SetScrollPos( wxHORIZONTAL, m_xScrollPosition );
  m_win->SetScrollPos( wxVERTICAL, m_yScrollPosition );
  m_targetWindow->ScrollWindow( old_x - new_x, old_y - new_y );
  AdjustScrollbars();
}

void wxScrollHelper::GetScrollPixelsPerUnit( int *x_unit, int *y_unit ) const {
  if( x_unit ) {
    *x_unit = m_xScrollPixelsPerLine;
  }
  if( y_unit ) {
    *y_unit = m_yScrollPixelsPerLine;
  }
}


int wxScrollHelper::GetScrollLines( int orient ) const {
  if( orient == wxHORIZONTAL ) {
    return m_xScrollLines;
  } else
  { return m_yScrollLines; }
}

int wxScrollHelper::GetScrollPageSize( int orient ) const {
  if( orient == wxHORIZONTAL ) {
    return m_xScrollLinesPerPage;
  } else
  { return m_yScrollLinesPerPage; }
}

void wxScrollHelper::SetScrollPageSize( int orient, int pageSize ) {
  if( orient == wxHORIZONTAL ) {
    m_xScrollLinesPerPage = pageSize;
  } else
  { m_yScrollLinesPerPage = pageSize; }
}

/*
   Scroll to given position (scroll position, not pixel position)
*/
void wxScrollHelper::Scroll( int x_pos, int y_pos ) {
  if( !m_targetWindow ) {
    return;
  }
  if( ( ( x_pos == -1 ) || ( x_pos == m_xScrollPosition ) ) &&
      ( ( y_pos == -1 ) || ( y_pos == m_yScrollPosition ) ) ) {
    return;
  }
  int w = 0, h = 0;
  GetTargetSize( &w, &h );
  int new_x = m_xScrollPosition;
  int new_y = m_yScrollPosition;
  if( ( x_pos != -1 ) && ( m_xScrollPixelsPerLine ) ) {
    new_x = x_pos;
    int noPagePositions = w / m_xScrollPixelsPerLine;
    if( noPagePositions < 1 ) {
      noPagePositions = 1;
    }
    new_x = wxMin( m_xScrollLines - noPagePositions, new_x );
    new_x = wxMax( 0, new_x );
  }
  if( ( y_pos != -1 ) && ( m_yScrollPixelsPerLine ) ) {
    new_y = y_pos;
    int noPagePositions = h / m_yScrollPixelsPerLine;
    if( noPagePositions < 1 ) {
      noPagePositions = 1;
    }
    new_y = wxMin( m_yScrollLines - noPagePositions, new_y );
    new_y = wxMax( 0, new_y );
  }
  if( new_x == m_xScrollPosition && new_y == m_yScrollPosition ) {
    return;
  }
  m_targetWindow->Update();
  if( m_xScrollPosition != new_x ) {
    int old_x = m_xScrollPosition;
    m_xScrollPosition = new_x;
    m_win->SetScrollPos( wxHORIZONTAL, new_x );
    m_targetWindow->ScrollWindow( ( old_x - new_x )*m_xScrollPixelsPerLine, 0,
                                  GetScrollRect() );
  }
  if( m_yScrollPosition != new_y ) {
    int old_y = m_yScrollPosition;
    m_yScrollPosition = new_y;
    m_win->SetScrollPos( wxVERTICAL, new_y );
    m_targetWindow->ScrollWindow( 0, ( old_y - new_y )*m_yScrollPixelsPerLine,
                                  GetScrollRect() );
  }
}

void wxScrollHelper::EnableScrolling( bool x_scroll, bool y_scroll ) {
  m_xScrollingEnabled = x_scroll;
  m_yScrollingEnabled = y_scroll;
}


void wxScrollHelper::GetViewStart( int *x, int *y ) const {
  if( x ) {
    *x = m_xScrollPosition;
  }
  if( y ) {
    *y = m_yScrollPosition;
  }
}

void wxScrollHelper::DoCalcScrolledPosition( int x, int y, int *xx, int *yy ) const {
  if( xx ) {
    *xx = x - m_xScrollPosition * m_xScrollPixelsPerLine;
  }
  if( yy ) {
    *yy = y - m_yScrollPosition * m_yScrollPixelsPerLine;
  }
}

void wxScrollHelper::DoCalcUnscrolledPosition( int x, int y, int *xx, int *yy ) const {
  if( xx ) {
    *xx = x + m_xScrollPosition * m_xScrollPixelsPerLine;
  }
  if( yy ) {
    *yy = y + m_yScrollPosition * m_yScrollPixelsPerLine;
  }
}





bool wxScrollHelper::ScrollLayout() {
  if( m_win->GetSizer() && m_targetWindow == m_win ) {
    int x = 0, y = 0, w = 0, h = 0;
    CalcScrolledPosition( 0, 0, &x, &y );
    m_win->GetVirtualSize( &w, &h );
    m_win->GetSizer()->SetDimension( x, y, w, h );
    return true;
  }
  return m_win->wxWindow::Layout();
}

void wxScrollHelper::ScrollDoSetVirtualSize( int x, int y ) {
  m_win->wxWindow::DoSetVirtualSize( x, y );
  AdjustScrollbars();
  if( m_win->GetAutoLayout() ) {
    m_win->Layout();
  }
}



wxSize wxScrollHelper::ScrollGetBestVirtualSize() const {
  wxSize clientSize( m_win->GetClientSize() );
  if( m_win->GetSizer() ) {
    clientSize.IncTo( m_win->GetSizer()->CalcMin() );
  }
  return clientSize;
}


wxSize
wxScrollHelper::ScrollGetWindowSizeForVirtualSize( const wxSize& size ) const {
  int ppuX, ppuY;
  GetScrollPixelsPerUnit( &ppuX, &ppuY );
  wxSize minSize = m_win->GetMinSize();
  wxSize best( size );
  if( ppuX > 0 ) {
    best.x = minSize.x + wxSystemSettings::GetMetric( wxSYS_VSCROLL_X );
  }
  if( ppuY > 0 ) {
    best.y = minSize.y + wxSystemSettings::GetMetric( wxSYS_HSCROLL_Y );
  }
  return best;
}






void wxScrollHelper::HandleOnSize( wxSizeEvent & ( event ) ) {
  if( m_targetWindow->GetAutoLayout() ) {
    wxSize size = m_targetWindow->GetBestVirtualSize();
    m_win->SetVirtualSize( size );
  } else {
    AdjustScrollbars();
  }
}



void wxScrollHelper::HandleOnPaint( wxPaintEvent & ( event ) ) {
  wxPaintDC dc( m_win );
  DoPrepareDC( dc );
  OnDraw( dc );
}





void wxScrollHelper::HandleOnChar( wxKeyEvent& event ) {
  int stx = 0, sty = 0,
      szx = 0, szy = 0,
      clix = 0, cliy = 0;
  GetViewStart( &stx, &sty );
  GetTargetSize( &clix, &cliy );
  m_targetWindow->GetVirtualSize( &szx, &szy );
  if( m_xScrollPixelsPerLine ) {
    clix /= m_xScrollPixelsPerLine;
    szx /= m_xScrollPixelsPerLine;
  } else {
    clix = 0;
    szx = -1;
  }
  if( m_yScrollPixelsPerLine ) {
    cliy /= m_yScrollPixelsPerLine;
    szy /= m_yScrollPixelsPerLine;
  } else {
    cliy = 0;
    szy = -1;
  }
  int xScrollOld = m_xScrollPosition,
      yScrollOld = m_yScrollPosition;
  int dsty;
  switch( event.GetKeyCode() ) {
    case WXK_PAGEUP:
      dsty = sty - ( 5 * cliy / 6 );
      Scroll( -1, ( dsty == -1 ) ? 0 : dsty );
      break;
    case WXK_PAGEDOWN:
      Scroll( -1, sty + ( 5 * cliy / 6 ) );
      break;
    case WXK_HOME:
      Scroll( 0, event.ControlDown() ? 0 : -1 );
      break;
    case WXK_END:
      Scroll( szx - clix, event.ControlDown() ? szy - cliy : -1 );
      break;
    case WXK_UP:
      Scroll( -1, sty - 1 );
      break;
    case WXK_DOWN:
      Scroll( -1, sty + 1 );
      break;
    case WXK_LEFT:
      Scroll( stx - 1, -1 );
      break;
    case WXK_RIGHT:
      Scroll( stx + 1, -1 );
      break;
    default:
      event.Skip();
  }
  if( m_xScrollPosition != xScrollOld ) {
    wxScrollWinEvent event( wxEVT_SCROLLWIN_THUMBTRACK, m_xScrollPosition,
                            wxHORIZONTAL );
    event.SetEventObject( m_win );
    m_win->GetEventHandler()->ProcessEvent( event );
  }
  if( m_yScrollPosition != yScrollOld ) {
    wxScrollWinEvent event( wxEVT_SCROLLWIN_THUMBTRACK, m_yScrollPosition,
                            wxVERTICAL );
    event.SetEventObject( m_win );
    m_win->GetEventHandler()->ProcessEvent( event );
  }
}






bool wxScrollHelper::SendAutoScrollEvents( wxScrollWinEvent& event ) const {
  wxWindow *win = ( wxWindow * )event.GetEventObject();
  return win->HasScrollbar( event.GetOrientation() );
}

void wxScrollHelper::StopAutoScrolling() {
  #if wxUSE_TIMER
  if( m_timerAutoScroll ) {
    delete m_timerAutoScroll;
    m_timerAutoScroll = ( wxTimer * )NULL;
  }
  #endif
}

void wxScrollHelper::HandleOnMouseEnter( wxMouseEvent& event ) {
  StopAutoScrolling();
  event.Skip();
}

void wxScrollHelper::HandleOnMouseLeave( wxMouseEvent& event ) {
  event.Skip();
  if( wxWindow::GetCapture() == m_targetWindow ) {
    int pos, orient;
    wxPoint pt = event.GetPosition();
    if( pt.x < 0 ) {
      orient = wxHORIZONTAL;
      pos = 0;
    } else if( pt.y < 0 ) {
      orient = wxVERTICAL;
      pos = 0;
    } else {
      wxSize size = m_targetWindow->GetClientSize();
      if( pt.x > size.x ) {
        orient = wxHORIZONTAL;
        pos = m_xScrollLines;
      } else if( pt.y > size.y ) {
        orient = wxVERTICAL;
        pos = m_yScrollLines;
      } else {
        return;
      }
    }
    if( !m_targetWindow->HasScrollbar( orient ) ) {
      return;
    }
    #if wxUSE_TIMER
    delete m_timerAutoScroll;
    m_timerAutoScroll = new wxAutoScrollTimer
    (
      m_targetWindow, this,
      pos == 0 ? wxEVT_SCROLLWIN_LINEUP
      : wxEVT_SCROLLWIN_LINEDOWN,
      pos,
      orient
    );
    m_timerAutoScroll->Start( 50 );
    #else
    wxUnusedVar( pos );
    #endif
  }
}

#if wxUSE_MOUSEWHEEL

void wxScrollHelper::HandleOnMouseWheel( wxMouseEvent& event ) {
  m_wheelRotation += event.GetWheelRotation();
  int lines = m_wheelRotation / event.GetWheelDelta();
  m_wheelRotation -= lines * event.GetWheelDelta();
  if( lines != 0 ) {
    wxScrollWinEvent newEvent;
    newEvent.SetPosition( 0 );
    newEvent.SetOrientation( wxVERTICAL );
    newEvent.SetEventObject( m_win );
    if( event.IsPageScroll() ) {
      if( lines > 0 ) {
        newEvent.SetEventType( wxEVT_SCROLLWIN_PAGEUP );
      } else
      { newEvent.SetEventType( wxEVT_SCROLLWIN_PAGEDOWN ); }
      m_win->GetEventHandler()->ProcessEvent( newEvent );
    } else {
      lines *= event.GetLinesPerAction();
      if( lines > 0 ) {
        newEvent.SetEventType( wxEVT_SCROLLWIN_LINEUP );
      } else
      { newEvent.SetEventType( wxEVT_SCROLLWIN_LINEDOWN ); }
      int times = abs( lines );
      for( ; times > 0; times-- ) {
        m_win->GetEventHandler()->ProcessEvent( newEvent );
      }
    }
  }
}

#endif

void wxScrollHelper::HandleOnChildFocus( wxChildFocusEvent& event ) {
  event.Skip();
  wxWindow *win = event.GetWindow();
  if( win == m_targetWindow ) {
    return;
  }
  wxWindow *actual_focus = wxWindow::FindFocus();
  if( win != actual_focus &&
      wxDynamicCast( win, wxPanel ) != 0 &&
      win->GetParent() == m_targetWindow )
  { return; }
  const wxRect viewRect( m_targetWindow->GetClientRect() );
  if( win->GetParent() != m_targetWindow ) {
    wxWindow *parent = win->GetParent();
    wxSize parent_size = parent->GetSize();
    if( parent_size.GetWidth() <= viewRect.GetWidth() &&
        parent_size.GetHeight() <= viewRect.GetHeight() )
    { win = parent; }
  }
  const wxRect
  winRect( m_targetWindow->ScreenToClient( win->GetScreenPosition() ),
           win->GetSize() );
  if( viewRect.Contains( winRect ) ) {
    return;
  }
  if( winRect.GetWidth() > viewRect.GetWidth() ||
      winRect.GetHeight() > viewRect.GetHeight() ) {
    return;
  }
  int stepx, stepy;
  GetScrollPixelsPerUnit( &stepx, &stepy );
  int startx, starty;
  GetViewStart( &startx, &starty );
  if( stepy > 0 ) {
    int diff = 0;
    if( winRect.GetTop() < 0 ) {
      diff = winRect.GetTop();
    } else if( winRect.GetBottom() > viewRect.GetHeight() ) {
      diff = winRect.GetBottom() - viewRect.GetHeight() + 1;
      diff += stepy - 1;
    }
    starty = ( starty * stepy + diff ) / stepy;
  }
  if( stepx > 0 ) {
    int diff = 0;
    if( winRect.GetLeft() < 0 ) {
      diff = winRect.GetLeft();
    } else if( winRect.GetRight() > viewRect.GetWidth() ) {
      diff = winRect.GetRight() - viewRect.GetWidth() + 1;
      diff += stepx - 1;
    }
    startx = ( startx * stepx + diff ) / stepx;
  }
  Scroll( startx, starty );
}

IMPLEMENT_DYNAMIC_CLASS( wxScrolledWindow, wxPanel )

BEGIN_EVENT_TABLE( wxScrolledWindow, wxPanel )
  EVT_PAINT( wxScrolledWindow::OnPaint )
END_EVENT_TABLE()

bool wxScrolledWindow::Create( wxWindow *parent,
                               wxWindowID id,
                               const wxPoint& pos,
                               const wxSize& size,
                               long style,
                               const wxString& name ) {
  m_targetWindow = this;
  bool ok = wxPanel::Create( parent, id, pos, size, style | wxHSCROLL | wxVSCROLL, name );
  return ok;
}

wxScrolledWindow::~wxScrolledWindow() {
}

void wxScrolledWindow::OnPaint( wxPaintEvent& event ) {
  m_handler->ResetDrawnFlag();
  event.Skip();
}
